package com.fsquare.service.paypal;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.validator.CreditCardValidator;
import org.broadleafcommerce.common.money.Money;
import org.broadleafcommerce.core.order.domain.Order;
import org.broadleafcommerce.core.payment.domain.CreditCardPaymentInfo;
import org.broadleafcommerce.core.payment.service.PaymentContext;
import org.broadleafcommerce.core.payment.service.type.PaymentInfoAdditionalFieldType;
import org.broadleafcommerce.core.web.order.CartState;
import org.broadleafcommerce.profile.core.domain.Customer;
import org.broadleafcommerce.profile.web.core.CustomerState;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.transaction.annotation.Transactional;

import com.fsquare.dao.paypal.PayPalDao;
import com.fsquare.domain.paypal.PayPalPayment;
import com.paypal.api.payments.Address;
import com.paypal.api.payments.Amount;
import com.paypal.api.payments.Authorization;
import com.paypal.api.payments.CreditCard;
import com.paypal.api.payments.Details;
import com.paypal.api.payments.FundingInstrument;
import com.paypal.api.payments.Links;
import com.paypal.api.payments.Payer;
import com.paypal.api.payments.Payment;
import com.paypal.api.payments.PaymentHistory;
import com.paypal.api.payments.RedirectUrls;
import com.paypal.api.payments.Refund;
import com.paypal.api.payments.Sale;
import com.paypal.api.payments.Transaction;
import com.paypal.core.rest.APIContext;
import com.paypal.core.rest.OAuthTokenCredential;
import com.paypal.core.rest.PayPalRESTException;
import com.paypal.core.rest.PayPalResource;
import com.paypal.core.rest.QueryParameters;

public class PayPalServiceImpl implements PayPalService{
	private static final Log LOG = LogFactory.getLog(PayPalServiceImpl.class);

	
	@Resource(name = "payPalDao")
    protected PayPalDao payPalDao;
	
	@Value("${paypal.client.id}")
    private String paypalClientid;
    
    @Value("${paypal.client.secret}")
    private String paypalClientSecret;
    
    
    public PayPalServiceImpl(){
    	initPayPal();
    }
    
    public Map<String, Object> creditCardPayment(PaymentContext paymentContext) {
    	
    	 Map<String, Object> result = new HashMap<String, Object>();
    	
    	CreditCardPaymentInfo ccInfo = (CreditCardPaymentInfo) paymentContext.getReferencedPaymentInfo();
    	if ((ccInfo == null) || (ccInfo.getPan() == null)) {
            return null;
        }
    	Order order = paymentContext.getPaymentInfo().getOrder();
        String nameOnCard = ccInfo.getNameOnCard();
        String ccNumber = ccInfo.getPan().replaceAll("[\\s-]+", "");
        Integer expMonth = ccInfo.getExpirationMonth();
        Integer expYear = ccInfo.getExpirationYear();
        String cvv = ccInfo.getCvvCode();
        
        String firstName = "";
        String [] names = nameOnCard.split(" ");
        for(int i = 0; i < (names.length-1); i++){
        	firstName = firstName+names[i]+" ";
        }
    	
        String cardType = validateCardType(ccNumber);
        if(cardType == null)
        	return null;
        
        cardType = cardType.toLowerCase();
    	
		// ###Address
		// Base Address object used as shipping or billing
		// address in a payment. [Optional]
//        paymentContext.getPaymentInfo().getAddress().getCity()
        org.broadleafcommerce.profile.core.domain.Address address = paymentContext.getPaymentInfo().getAddress();
		Address billingAddress = new Address();
		billingAddress.setCity(address.getCity());
		billingAddress.setCountryCode(address.getCountry().getAbbreviation());
		billingAddress.setLine1(address.getAddressLine1());
		billingAddress.setLine2(address.getAddressLine2());
		billingAddress.setPostalCode(address.getPostalCode());
		if(address.getState() != null)
			billingAddress.setState(address.getState().getAbbreviation());

		// ###CreditCard
		// A resource representing a credit card that can be
		// used to fund a payment.
		CreditCard creditCard = new CreditCard();
		creditCard.setBillingAddress(billingAddress);
		creditCard.setCvv2(cvv);
		creditCard.setExpireMonth(expMonth);
		creditCard.setExpireYear(expYear);
		creditCard.setFirstName(firstName.trim());
		creditCard.setLastName(names[names.length-1]);
		creditCard.setNumber(ccNumber);
		creditCard.setType(cardType);
		
		
//		Map<String, String> additionalFields = new HashMap<String, String>();
//        additionalFields.put(PaymentInfoAdditionalFieldType.NAME_ON_CARD.getType(), nameOnCard);
//        additionalFields.put(PaymentInfoAdditionalFieldType.CARD_TYPE.getType(), cardType);
//        additionalFields.put(PaymentInfoAdditionalFieldType.EXP_MONTH.getType(), expMonth.toString());
//        additionalFields.put(PaymentInfoAdditionalFieldType.EXP_YEAR.getType(), expYear.toString());
//        additionalFields.put(PaymentInfoAdditionalFieldType.LAST_FOUR.getType(), StringUtils.right(ccNumber, 4));
		
        result.put(RESULT_CREDIT_CARD_INFO, creditCard);
		

		
		Money money = paymentContext.getTransactionAmount();
		// ###Details
		// Let's you specify details of a payment amount.
		Details details = new Details();
		details.setShipping(order.getTotalShipping().getAmount().toString());
		details.setSubtotal(order.getSubTotal().getAmount().toString());
		details.setTax(order.getTotalTax().getAmount().toString());
//		Object xx = locale;
		// ###Amount
		// Let's you specify a payment amount.
		Amount amount = new Amount();
		amount.setCurrency(money.getCurrency().getCurrencyCode());
		// Total must be equal to sum of shipping, tax and subtotal.
		amount.setTotal(money.getAmount().toString());
		amount.setDetails(details);

		// ###Transaction
		// A transaction defines the contract of a
		// payment - what is the payment for and who
		// is fulfilling it. Transaction is created with
		// a `Payee` and `Amount` types
		Transaction transaction = new Transaction();
		transaction.setAmount(amount);
		transaction.setDescription("This is the payment transaction description.");

		// The Payment creation API requires a list of
		// Transaction; add the created `Transaction`
		// to a List
		List<Transaction> transactions = new ArrayList<Transaction>();
		transactions.add(transaction);

		// ###FundingInstrument
		// A resource representing a Payeer's funding instrument.
		// Use a Payer ID (A unique identifier of the payer generated
		// and provided by the facilitator. This is required when
		// creating or using a tokenized funding instrument)
		// and the `CreditCardDetails`
		FundingInstrument fundingInstrument = new FundingInstrument();
		fundingInstrument.setCreditCard(creditCard);

		// The Payment creation API requires a list of
		// FundingInstrument; add the created `FundingInstrument`
		// to a List
		List<FundingInstrument> fundingInstrumentList = new ArrayList<FundingInstrument>();
		fundingInstrumentList.add(fundingInstrument);

		// ###Payer
		// A resource representing a Payer that funds a payment
		// Use the List of `FundingInstrument` and the Payment Method
		// as 'credit_card'
		Payer payer = new Payer();
		payer.setFundingInstruments(fundingInstrumentList);
		payer.setPaymentMethod(PAYPAL_PAYMENT_METHOD_CREDIT_CARD);

		// ###Payment
		// A Payment Resource; create one using
		// the above types and intent as 'sale'
		Payment payment = new Payment();
		payment.setIntent(PAYPAL_INTENT_SALE);
		payment.setPayer(payer);
		payment.setTransactions(transactions);

		try {
			// ###AccessToken
			// Retrieve the access token from
			// OAuthTokenCredential by passing in
			// ClientID and ClientSecret
			// It is not mandatory to generate Access Token on a per call basis.
			// Typically the access token can be generated once and
			// reused within the expiry window
			String accessToken = getAccessToken();

			// ### Api Context
			// Pass in a `ApiContext` object to authenticate 
			// the call and to send a unique request id 
			// (that ensures idempotency). The SDK generates
			// a request id if you do not pass one explicitly. 
			APIContext apiContext = new APIContext(accessToken);
			result.put(RESULT_ACCESS_TOKEN, accessToken);
			// Use this variant if you want to pass in a request id  
			// that is meaningful in your application, ideally 
			// a order id.
			/* 
			 * String requestId = Long.toString(System.nanoTime();
			 * APIContext apiContext = new APIContext(accessToken, requestId ));
			 */
			
			// Create a payment by posting to the APIService
			// using a valid AccessToken
			// The return object contains the status;
			Payment createdPayment = payment.create(apiContext);
			result.put(RESULT_PAYMENT, createdPayment);
			
//			createdPayment.
			
			
//			req.setAttribute("response", Payment.getLastResponse());
			LOG.info("Created payment with id = " + createdPayment.getId() + " and status = " + createdPayment.getState());
			return result;
//			boolean validDate = createdPayment.
			
//			if (validDate && validCard && validCVV){
//	            Map<String, String> additionalFields = new HashMap<String, String>();
//	            additionalFields.put(PaymentInfoAdditionalFieldType.NAME_ON_CARD.getType(), nameOnCard);
//	            additionalFields.put(PaymentInfoAdditionalFieldType.CARD_TYPE.getType(), cardType);
//	            additionalFields.put(PaymentInfoAdditionalFieldType.EXP_MONTH.getType(), expMonth+"");
//	            additionalFields.put(PaymentInfoAdditionalFieldType.EXP_YEAR.getType(), expYear+"");
//	            additionalFields.put(PaymentInfoAdditionalFieldType.LAST_FOUR.getType(), StringUtils.right(ccNumber, 4));
//	            return additionalFields;
//	        } else {
//	            return null;
//	        }
			
		} catch (PayPalRESTException e) {
			e.printStackTrace();
		}

		return null;
	}
    
    
    public String getAccessToken() throws PayPalRESTException {
    	OAuthTokenCredential oAuthTokenCredential = new OAuthTokenCredential(paypalClientid, paypalClientSecret);
//    	oAuthTokenCredential.
    	String accessToken = oAuthTokenCredential.getAccessToken();
    	LOG.info("PAYPAL ACCESS TOKEN: "+accessToken);
		return accessToken;
	}
    
    private void initPayPal(){
    	System.out.println("--- initPayPal ---");
		InputStream is = PayPalServiceImpl.class.getResourceAsStream("/sdk_config.properties");
		try {
			PayPalResource.initConfig(is);
		} catch (PayPalRESTException e) {
			e.printStackTrace();
			LOG.fatal(e.getMessage());
		}
	}
    
    public String validateCardType(PaymentContext paymentContext){
    	CreditCardPaymentInfo ccInfo = (CreditCardPaymentInfo) paymentContext.getReferencedPaymentInfo();
//    	Order order = paymentContext.getPaymentInfo().getOrder();
//        String nameOnCard = ccInfo.getNameOnCard();
        String ccNumber = ccInfo.getPan().replaceAll("[\\s-]+", "");
//        Integer expMonth = ccInfo.getExpirationMonth();
//        Integer expYear = ccInfo.getExpirationYear();
//        String cvv = ccInfo.getCvvCode();
    	return validateCardType(ccNumber);
    }
    public String validateCardType(String ccNumber){
        CreditCardValidator visaValidator = new CreditCardValidator(CreditCardValidator.VISA);
        CreditCardValidator amexValidator = new CreditCardValidator(CreditCardValidator.AMEX);
        CreditCardValidator mcValidator = new CreditCardValidator(CreditCardValidator.MASTERCARD);
        CreditCardValidator discoverValidator = new CreditCardValidator(CreditCardValidator.DISCOVER);

        if (visaValidator.isValid(ccNumber)){
            return "VISA";
        } else if (amexValidator.isValid(ccNumber)) {
            return "AMEX";
        } else if (mcValidator.isValid(ccNumber)) {
            return "MASTERCARD";
        } else if (discoverValidator.isValid(ccNumber)) {
            return "DISCOVER";
        }
        return "UNKNOWN";
    }

	@Override
	public String getPaymentHystory(String quantity) {
		QueryParameters queryParameters = new QueryParameters();
		queryParameters.setCount(quantity);
		try {
			
			// ###AccessToken
			// Retrieve the access token from
			// OAuthTokenCredential by passing in
			// It is not mandatory to generate Access Token on a per call basis.
			// Typically the access token can be generated once and
			// reused within the expiry window
			String accessToken = getAccessToken();

			// ###Retrieve
			// Retrieve the PaymentHistory object by calling the
			// static `get` method
			// on the Payment class, and pass the
			// AccessToken and a QueryParameters object that contains
			// query parameters for paginations and filtering.
			// Refer the API documentation
			// for valid values for keys
			PaymentHistory paymentHistory = Payment.get(accessToken, queryParameters);
			LOG.info("Payment History = " + paymentHistory.toString());
			return Payment.getLastResponse();
		} catch (PayPalRESTException e) {
			e.printStackTrace();
		}
		return null;
	}

	@Override
	public Sale getSale(String saleId) {
		try {
			// ###AccessToken
			// Retrieve the access token from
			// OAuthTokenCredential by passing in
			// ClientID and ClientSecret
			// It is not mandatory to generate Access Token on a per call basis.
			// Typically the access token can be generated once and
			// reused within the expiry window
			String accessToken = getAccessToken();

			// Pass an AccessToken and the ID of the sale
			// transaction from your payment resource.
			Sale sale = Sale.get(accessToken, saleId);
			LOG.info("Sale amount : " + sale.getAmount() + " for saleID : "+ sale.getId());
			return sale;
		} catch (PayPalRESTException e) {
			e.printStackTrace();
		}
		return null;
	}

	@Override
	public String saleRefund(Sale sale) {
		// ###Sale
		// A sale transaction.
		// Create a Sale object with the
		// given sale transaction id.
		

		// ###Refund
		// A refund transaction.
		// Use the amount to create
		// a refund object
		 Refund refund = new Refund();
		// ###Amount
		// Create an Amount object to
		// represent the amount to be
		// refunded. Create the refund object, if the refund is partial
//		 Amount amount = new Amount();
//		 amount.setCurrency("USD");
//		 amount.setTotal("0.01");
		 refund.setAmount(sale.getAmount());
		try {
			// ###AccessToken
			// Retrieve the access token from
			// OAuthTokenCredential by passing in
			// ClientID and ClientSecret
			// It is not mandatory to generate Access Token on a per call basis.
			// Typically the access token can be generated once and
			// reused within the expiry window
			String accessToken = getAccessToken();

			// ### Api Context
			// Pass in a `ApiContext` object to authenticate 
			// the call and to send a unique request id 
			// (that ensures idempotency). The SDK generates
			// a request id if you do not pass one explicitly. 
			APIContext apiContext = new APIContext(accessToken);
			// Use this variant if you want to pass in a request id  
			// that is meaningful in your application, ideally 
			// a order id.
			/* 
			 * String requestId = Long.toString(System.nanoTime();
			 * APIContext apiContext = new APIContext(accessToken, requestId ));
			 */
			
			// Refund by posting to the APIService
			// using a valid AccessToken
			sale.refund(apiContext, refund);
			return Sale.getLastResponse();
		} catch (PayPalRESTException e) {
			e.printStackTrace();
		}
		
		return null;
	}
//	@Override
//	public String payPalPayment(PaymentContext paymentContext){
//		return null;
//	}

	@Override
	public String payPalPayment(HttpServletRequest request) {
		
		Customer customer = CustomerState.getCustomer();
		if(customer.isAnonymous())
			return null;
		
		// ###AccessToken
		// Retrieve the access token from
		// OAuthTokenCredential by passing in
		// ClientID and ClientSecret
		APIContext apiContext = null;
		String accessToken = null;
		try {
			accessToken = getAccessToken();

			// ### Api Context
			// Pass in a `ApiContext` object to authenticate
			// the call and to send a unique request id
			// (that ensures idempotency). The SDK generates
			// a request id if you do not pass one explicitly.
			apiContext = new APIContext(accessToken);
			// Use this variant if you want to pass in a request id
			// that is meaningful in your application, ideally
			// a order id.
			/*
			 * String requestId = Long.toString(System.nanoTime(); APIContext
			 * apiContext = new APIContext(accessToken, requestId ));
			 */
		} catch (PayPalRESTException e) {
			e.printStackTrace();
		}
//		if (req.getParameter("PayerID") != null) {
//			Payment payment = new Payment();
//			if (req.getParameter("guid") != null) {
//				payment.setId(map.get(req.getParameter("guid")));
//			}
//
//			PaymentExecution paymentExecution = new PaymentExecution();
//			paymentExecution.setPayerId(customer.getUsername());
//			try {
//				payment.execute(apiContext, paymentExecution);
//				req.setAttribute("response", Payment.getLastResponse());
//			} catch (PayPalRESTException e) {
//				req.setAttribute("error", e.getMessage());
//			}
//		} else {

			// ###Details
			// Let's you specify details of a payment amount.
		
			Order cart = CartState.getCart();
//			PaymentInfo ccInfo = creditCardPaymentInfoFactory.constructPaymentInfo(order);
			Details details = new Details();
			details.setShipping(cart.getTotalShipping().toString());
			details.setSubtotal(cart.getSubTotal().toString());
//			details.setTax("1");

			// ###Amount
			// Let's you specify a payment amount.
			Amount amount = new Amount();
			amount.setCurrency(cart.getCurrency().getCurrencyCode());
			// Total must be equal to sum of shipping, tax and subtotal.
			amount.setTotal(cart.getTotal().toString());
			amount.setDetails(details);

			// ###Transaction
			// A transaction defines the contract of a
			// payment - what is the payment for and who
			// is fulfilling it. Transaction is created with
			// a `Payee` and `Amount` types
			Transaction transaction = new Transaction();
			transaction.setAmount(amount);
			transaction.setDescription("This is the payment transaction description.");

			// The Payment creation API requires a list of
			// Transaction; add the created `Transaction`
			// to a List
			List<Transaction> transactions = new ArrayList<Transaction>();
			transactions.add(transaction);

			// ###Payer
			// A resource representing a Payer that funds a payment
			// Payment Method
			// as 'paypal'
			Payer payer = new Payer();
			payer.setPaymentMethod(PAYPAL_PAYMENT_METHOD_PAY_PAL);

			// ###Payment
			// A Payment Resource; create one using
			// the above types and intent as 'sale'
			Payment payment = new Payment();
			payment.setIntent(PAYPAL_INTENT_SALE);
			payment.setPayer(payer);
			payment.setTransactions(transactions);

			// ###Redirect URLs
			RedirectUrls redirectUrls = new RedirectUrls();
//			String guid = UUID.randomUUID().toString().replaceAll("-", "");
			
			String guid = "fsquareX"+System.currentTimeMillis()+"X"+customer.getId();
			
			redirectUrls.setCancelUrl(request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort()+ request.getContextPath() + "/checkout/payPalPayment/cancel?guid=" + guid);
			redirectUrls.setReturnUrl(request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath() + "/checkout/payPalPayment/ret?guid=" + guid);
			
			payment.setRedirectUrls(redirectUrls);

			// Create a payment by posting to the APIService
			// using a valid AccessToken
			// The return object contains the status;
			try {
				Payment createdPayment = payment.create(apiContext);
				LOG.info("Created payment with id = "+ createdPayment.getId() + " and status = "+ createdPayment.getState());
				// ###Payment Approval Url
				Iterator<Links> links = createdPayment.getLinks().iterator();
				String url = "";
				while (links.hasNext()) {
					Links link = links.next();
					if (link.getRel().equalsIgnoreCase("approval_url")) {
						url = link.getHref();
					}
				}
				
				LOG.info("PAYMENT: "+Payment.getLastResponse());
//				request.setAttribute("response", Payment.getLastResponse());
//				map.put(guid, createdPayment.getId());
//				JSONObject jsonObj = new JSONObject(Payment.getLastResponse());
				PayPalPayment payPalPayment = payPalDao.createPayPalPayment();
				payPalPayment.setPaymentId(createdPayment.getId());
				payPalPayment.setGuid(guid);
				payPalPayment.setCustomer(customer);
				payPalPayment.setRequestDate(new Date());
				payPalPayment.setAmount(transaction.getAmount().getTotal());
				payPalPayment.setCurrencyCode(amount.getCurrency());
				payPalPayment.setPaymentURL(url);
//				payPalPayment = payPalDao.save(payPalPayment);
				savePayPalPayment(payPalPayment);
				
				LOG.info("Payment URL: "+url);
				
				return url;
			
			
			} catch (PayPalRESTException e) {
				e.printStackTrace();
			}
//		}
//		req.setAttribute("request", Payment.getLastRequest());
//		req.getRequestDispatcher("response.jsp").forward(req, resp);
		
		
		
		return null;
	}
	
	@Transactional
	public PayPalPayment savePayPalPayment(PayPalPayment payPalPayment){
		payPalPayment = payPalDao.save(payPalPayment);
		return payPalPayment;
	}

	@Override
	public PayPalPayment payPalPaymentReturn(HttpServletRequest request) {
		String guid = request.getParameter("guid");
		PayPalPayment payPalPayment = payPalDao.getPaymentsByGuid(guid);
		payPalPayment.setState(PayPalPayment.State.SUCCESS);
		payPalPayment = payPalDao.save(payPalPayment);
		return payPalPayment;
	}

	@Override
	public PayPalPayment payPalPaymentCancel(HttpServletRequest request) {
		String guid = request.getParameter("guid");
		PayPalPayment payPalPayment = payPalDao.getPaymentsByGuid(guid);
		payPalPayment.setState(PayPalPayment.State.CANCELED);
		payPalPayment = payPalDao.save(payPalPayment);
		return payPalPayment;
	}
	
	@Override
	public Authorization getAuthorization(PaymentContext paymentContext)throws PayPalRESTException {
//		Order cart = CartState.getCart();
		CreditCardPaymentInfo ccInfo = (CreditCardPaymentInfo) paymentContext.getReferencedPaymentInfo();
    	if ((ccInfo == null) || (ccInfo.getPan() == null)) {
            return null;
        }
    	Order order = paymentContext.getPaymentInfo().getOrder();
        String nameOnCard = ccInfo.getNameOnCard();
        String ccNumber = ccInfo.getPan().replaceAll("[\\s-]+", "");
        Integer expMonth = ccInfo.getExpirationMonth();
        Integer expYear = ccInfo.getExpirationYear();
        String cvv = ccInfo.getCvvCode();
        
        String firstName = "";
        String [] names = nameOnCard.split(" ");
        for(int i = 0; i < (names.length-1); i++){
        	firstName = firstName+names[i]+" ";
        }
    	
        String cardType = validateCardType(ccNumber);
        if(cardType == null)
        	return null;
        
        Money money = paymentContext.getTransactionAmount();
		// ###Details
		// Let's you specify details of a payment amount.
		Details details = new Details();
		details.setShipping(order.getTotalShipping().getAmount().toString());
		details.setSubtotal(order.getSubTotal().getAmount().toString());
		details.setTax(order.getTotalTax().getAmount().toString());
//		Object xx = locale;
		// ###Amount
		// Let's you specify a payment amount.
		Amount amount = new Amount();
		amount.setCurrency(money.getCurrency().getCurrencyCode());
		// Total must be equal to sum of shipping, tax and subtotal.
		amount.setTotal(money.getAmount().toString());
		amount.setDetails(details);
        
//        cardType = cardType.toLowerCase();
//		// ###Details
//		// Let's you specify details of a payment amount.
//		Details details = new Details();
//		details.setShipping("0.03");
//		details.setSubtotal("107.41");
//		details.setTax("0.03");
//
//		// ###Amount
//		// Let's you specify a payment amount.
//		Amount amount = new Amount();
//		amount.setCurrency("USD");
//		amount.setTotal("107.47");
//		amount.setDetails(details);

		// ###Transaction
		// A transaction defines the contract of a
		// payment - what is the payment for and who
		// is fulfilling it. Transaction is created with
		// a `Payee` and `Amount` types
		Transaction transaction = new Transaction();
		transaction.setAmount(amount);
		transaction.setDescription("This is the payment transaction description.");
		
		// The Payment creation API requires a list of
		// Transaction; add the created `Transaction`
		// to a List
		List<Transaction> transactions = new ArrayList<Transaction>();
		transactions.add(transaction);

		// ###Address
		// Base Address object used as shipping or billing
		// address in a payment. [Optional]
		
		org.broadleafcommerce.profile.core.domain.Address address = paymentContext.getPaymentInfo().getAddress();
		Address billingAddress = new Address();
		billingAddress.setCity(address.getCity());
		billingAddress.setCountryCode(address.getCountry().getAbbreviation());
		billingAddress.setLine1(address.getAddressLine1());
		billingAddress.setLine2(address.getAddressLine2());
		billingAddress.setPostalCode(address.getPostalCode());
		if(address.getState() != null)
			billingAddress.setState(address.getState().getAbbreviation());

		// ###CreditCard
		// A resource representing a credit card that can be
		// used to fund a payment.
		CreditCard creditCard = new CreditCard();
		creditCard.setBillingAddress(billingAddress);
		creditCard.setCvv2(cvv);
		creditCard.setExpireMonth(expMonth);
		creditCard.setExpireYear(expYear);
		creditCard.setFirstName(firstName.trim());
		creditCard.setLastName(names[names.length-1]);
		creditCard.setNumber(ccNumber);
		creditCard.setType(cardType);
		
		// ###FundingInstrument
		// A resource representing a Payeer's funding instrument.
		// Use a Payer ID (A unique identifier of the payer generated
		// and provided by the facilitator. This is required when
		// creating or using a tokenized funding instrument)
		// and the `CreditCardDetails`
		FundingInstrument fundingInstrument = new FundingInstrument();
		fundingInstrument.setCreditCard(creditCard);
		
		// The Payment creation API requires a list of
		// FundingInstrument; add the created `FundingInstrument`
		// to a List
		List<FundingInstrument> fundingInstruments = new ArrayList<FundingInstrument>();
		fundingInstruments.add(fundingInstrument);
		
		// ###Payer
		// A resource representing a Payer that funds a payment
		// Use the List of `FundingInstrument` and the Payment Method
		// as 'credit_card'
		Payer payer = new Payer();
		payer.setFundingInstruments(fundingInstruments);
		payer.setPaymentMethod(PAYPAL_PAYMENT_METHOD_CREDIT_CARD);

		// ###Payment
		// A Payment Resource; create one using
		// the above types and intent as 'authorize'
		Payment payment = new Payment();
		payment.setIntent(PAYPAL_INTENT_AUTHORIZE);
		payment.setPayer(payer);
		payment.setTransactions(transactions);

		
		String accessToken = getAccessToken();
		APIContext apiContext = new APIContext(accessToken);
		Payment responsePayment = payment.create(apiContext);
		Authorization authorization = responsePayment.getTransactions().get(0).getRelatedResources().get(0).getAuthorization();
		return authorization;
	}
}
